
/**
 * @author Johannes Riedmann
 * @author Lukas K�ll
 * @author David Medenjak
 */
public class Fabrik {

    /**
     * contains all robots in the factory
     */
    private Map roboters;
    /**
     * unique id counter for the robots
     */
    private static int id = 0;
    /**
     * the factories name
     */
    private String name;

    /**
     * Construcor.
     * @param name Name of the fabrik.
     */
    public Fabrik(String name) {
        this.name = name;
        this.roboters = new Map();
    }

    /**
     * Factory pattern - creates a predefined robot
     * @param type Descriptive name of the robot.
     * @return Returns the new Robot.
     */
    public Roboter createRoboter(String type) {
        id++;
        if (type.equals("RaupenSchweissRoboter")) {
            OperationMode b = new SchweissRoboter();
            Roboter ret = new RaupenRoboter(id, b);
            return ret;

        } else if (type.equals("RaupenLackierRoboter")) {
            OperationMode b = new LackierRoboter();
            Roboter ret = new RaupenRoboter(id, b);
            return ret;
        } else if (type.equals("SchwenkarmSchweissRoboter")) {
            OperationMode b = new SchweissRoboter();
            Roboter ret = new SchwenkarmRoboter(id, b);
            return ret;
        } else if (type.equals("SchwenkarmLackierRoboter")) {
            OperationMode b = new LackierRoboter();
            Roboter ret = new SchwenkarmRoboter(id, b);
            return ret;
        } else {
            throw new IllegalArgumentException("The robot type " + type
                    + " does not exist"
                    + " and can therefore not be instantiated.");
        }
    }

    /**
     * Adds a robot to the factory
     * @param roboter Roboter to add.
     */
    public void addRoboter(Roboter roboter) {
        System.out.println("Adding to " + name + ": " +roboter);
        roboters.add(roboter.getId(), roboter);
    }

    /**
     * Removes a robot from the factory
     * @param roboterId Roboter to remove's ID.
     */
    public void removeRoboter(int roboterId) {
        roboters.remove(roboterId);
    }

    /**
     * Changes a robots operation mode.
     * @param roboterId Roboter to change's ID.
     * @param operationMode New mode to set as string.
     */
    public void changeRoboterOperationMode(int roboterId, String operationMode) {
        Roboter roboter = (Roboter) roboters.get(roboterId);
        OperationMode mode = null;
        if (operationMode.equals("SchweissRoboter")) {
            mode = new SchweissRoboter();
        } else if (operationMode.equals("LackierRoboter")) {
            mode = new LackierRoboter();
        } else {
            throw new IllegalArgumentException("The operation mode " + operationMode
                    + " does not exist and can therefore not be instantiated.");
        }
        roboter.setOperationMode(mode);
    }

    /**
     * @return the average operating hours of all robots in this factory
     */
    public double getAverageOperatingHours() {
        return averageOf("", "", "OperatingHours");
    }

    /**
     * @param mode The type of robots we want to have in our statistic
     * @return average operating hours for all robots of the OperationMode $type.
     */
    public double getAverageOperatingHoursForOperationMode(String mode) {
        return averageOf("", mode, "OperatingHours");
    }

    /**
     * @param type The classname of the robots that we want in the statistic.
     * @return average operating hours for all robots of the type $roboter
     */
    public double getAverageOperatingHoursForRobotertype(String type) {
        return averageOf(type, "", "OperatingHours");
    }

    /**
     * @return the average rotations of all SchwenkarmRoboter's in this fabrik
     */
    public double getAverageRotations() {
        return averageOf("SchwenkarmRoboter", "", "Rotationen");
    }

    /**
     * @param mode String representation of a OperationMode-Type
     * @return average rotations of all SchwenkarmRoboters with operation mode as
     *         specified in param
     */
    public double getAverageRotationsForOperationMode(String mode) {
        return averageOf("SchwenkarmRoboter", mode, "Rotationen");
    }

    /**
     * Calculating minimum working temperature.
     * @return minimum working temperature of the robots in the fabrik.
     */
    public double getMinimumWorkingTemperature() {
        return minimumOf("", "SchweissRoboter", "Temperature");
    }

    /**
     * Calculating minimum working temperature.
     * @param type The classname of the robots that we want in the statistic.
     * @return minimum working temperature of the robots in the fabrik, matching other parameters.
     */
    public double getMinimumWorkingTemperatureForRobotertyp(String type) {
        return minimumOf(type, "SchweissRoboter", "Temperature");
    }

    /**
     * Calculating maximum working temperature.
     * @return Maximum working temperature of the robots in the fabrik.
     */
    public int getMaximumWorkingTemperature() {
        return (int) maximumOf("", "SchweissRoboter", "Temperature");
    }

    /**
     * @param type The classname of the robots that we want in the statistic.
     * @return Maximum working temperature of the robots in the fabrik, matching other parameters.
     */
    public int getMaximumWorkingTemperatureForRobotertyp(String type) {
        return (int) maximumOf(type, "SchweissRoboter", "Temperature");
    }

    /**
     * @return Returns average way distance of all Raupenroboters.
     */
    public float getAverageWay() {
        return (float) averageOf("RaupenRoboter", "", "Way");
    }

    /**
     * @param mode String representation of a OperationMode-Type
     * @return average way distance of all Raupenroboters with operation mode as
     *         specified in param
     */
    public float getAverageWayForOperationMode(String mode) {
        return (float) averageOf("RaupenRoboter", mode, "Way");
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * Gets the average of the specified element $what from the robots in the fabrik.
     * @param type Type the robot has to be.
     * @param mode Mode the robot has to be.
     * @param what What we want to find.
     * @return Returns average of the robots matching parameters.
     */
    private double averageOf(String type, String mode, String what) {
        double sum = 0;
        double count = 0;
        Iter it = roboters.iterator();
        while (it.hasNext()) {
            Roboter next = (Roboter) it.next();
            if (type.equals("") || type.equals(next.getType())) {
                if (mode.equals("") || mode.equals(next.getOperationModeName())) {
                    if (what.equals("")) {
                        throw new IllegalArgumentException("Thing to evaluate not specified.");
                    } else if (what.equals("OperatingHours")) {
                        sum += next.getOperatingHours();
                        count++;
                    } else if (what.equals("Rotationen")) {
                        sum += ((SchwenkarmRoboter) next).getRotationen();
                        count++;
                    } else if (what.equals("Way")) {
                        sum += ((RaupenRoboter) next).getWegstrecke();
                        count++;
                    } else if (what.equals("Temperature")) {
                        sum += ((SchweissRoboter) next.getOperationMode()).getWorkingTemperature();
                        count++;
                    } else {
                        System.out.println(what + " not supported.");
                    }
                }
            }
        }
        return sum / count;
    }

    /**
     * Gets the minimum of the specified element $what from the robots in the fabrik.
     * @param type Type the robot has to be.
     * @param mode Mode the robot has to be.
     * @param what What we want to find.
     * @return Returns minimum of the robots matching parameters.
     */
    private double minimumOf(String type, String mode, String what) {
        double min = 9999;
        Iter it = roboters.iterator();
        while (it.hasNext()) {
            Roboter next = (Roboter) it.next();
            if (type.equals("") || type.equals(next.getType())) {
                if (mode.equals("") || mode.equals(next.getOperationModeName())) {
                    if (what.equals("")) {
                        throw new IllegalArgumentException("Thing to evaluate not specified.");
                    } else if (what.equals("Temperature")) {
                        double tmp = ((SchweissRoboter) next.getOperationMode()).getWorkingTemperature();
                        if (tmp < min) {
                            min = tmp;
                        }
                    } else {
                        System.out.println(what + " not supported.");
                    }
                }
            }
        }
        return min;
    }

    /**
     * Gets the maximum of the specified element $what from the robots in the fabrik.
     * @param type Type the robot has to be.
     * @param mode Mode the robot has to be.
     * @param what What we want to find.
     * @return Returns maximum of the robots matching parameters.
     */
    private double maximumOf(String type, String mode, String what) {
        double max = 0;
        Iter it = roboters.iterator();
        while (it.hasNext()) {
            Roboter next = (Roboter) it.next();
            if (type.equals("") || type.equals(next.getType())) {
                if (mode.equals("") || mode.equals(next.getOperationModeName())) {
                    if (what.equals("")) {
                        throw new IllegalArgumentException("Thing to evaluate not specified.");
                    } else if (what.equals("Temperature")) {
                        double tmp = ((SchweissRoboter) next.getOperationMode()).getWorkingTemperature();
                        if (tmp > max) {
                            max = tmp;
                        }
                    } else {
                        System.out.println(what + " not supported.");
                    }
                }
            }
        }
        return max;
    }

    /**
     * String representation of the object.
     * @return Returns a string, representing the object.
     */
    public String toString() {
        String ret = "Factory '" + name + "'\n";
        ret += "containing robots: \n";
        Iter it = roboters.iterator();
        
        for (int i = 0; i < roboters.getSize(); i++) {
            Roboter roboter = (Roboter) it.next();
            ret += roboter.getId() + ": " + roboter.getClass().getSimpleName() + ", " + roboter.getOperationModeName() + "\n";
        }
        
        return ret;
    }
}
